//-----------------------------------------------------------------------

// <copyright file="StackedLayoutStrategy.cs" company="Jamie Knight">

// Copyright (c) Jamie Knight. All rights reserved.

// </copyright>

//-----------------------------------------------------------------------

using DevKnightlie.WPF.AutoForm.FieldDefinitions;
using DevKnightlie.WPF.AutoForm.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;

namespace DevKnightlie.WPF.AutoForm
{

    /// <summary>
    /// Argument class passed to the SpacerControlRequired event
    /// </summary>
    public class StackedLayoutSpacerRequiredEventArgs
    {
        /// <summary>
        /// The Control object to use as a horizontal spacer
        /// </summary>
        public Control SpacerControl { get; set; }

        /// <summary>
        /// The field definition which requires a spacer.  The spacer control will appear after this field
        /// </summary>
        public BaseFieldDefinition PrecedingFieldDefinition { get; private set; }

        public StackedLayoutSpacerRequiredEventArgs(BaseFieldDefinition precedingFieldDefinition)
        {
            PrecedingFieldDefinition = precedingFieldDefinition;
        }
    }


    /// <summary>
    /// Layout strategy class which arranges input fields in a single column
    /// </summary>
    public class StackedLayoutStrategy : ILayoutStrategy
    {
        #region Private Fields

        private AutoFormPanel _formPanel;
        private Dictionary<BaseFieldDefinition, Control> _containerControls;
        private Dictionary<BaseFieldDefinition, Control> _fieldLabels;
        private Grid _controlsGrid;
        private double _rowPadding = 6;
        private double _columnPadding = 6;

        #endregion

        /// <summary>
        /// The form panel whose controls are being arranged
        /// </summary>
        public AutoFormPanel FormPanel
        {
            get
            {
                return _formPanel;
            }
            set
            {
                _formPanel = value;
            }
        }


        /// <summary>
        /// Sets or returns the space between rows
        /// </summary>
        public double RowSpacing
        {
            get
            {
                return _rowPadding;
            }
            set
            {
                _rowPadding = value;
                UpdateControlMargins();
            }
        }

        /// <summary>
        /// Sets or returns the space between columns
        /// </summary>
        public double ColumnSpacing
        {
            get
            {
                return _columnPadding;
            }
            set
            {
                _columnPadding = value;
                UpdateControlMargins();
            }
        }


        public enum FieldLabelPositionType
        {
            Left, Above
        };


        public FieldLabelPositionType FieldLabelPosition
        {
            get { return _fieldLabelPosition; }
            set { _fieldLabelPosition = value; }
        }

        #region Events

        public delegate void StackedLayoutSpacerRequiredEvent(object sender, StackedLayoutSpacerRequiredEventArgs e);

        private StackedLayoutSpacerRequiredEvent _spacerRequired;

        /// <summary>
        /// Fired when the strategy object needs a control to use as a spacer. Handler should return the spacer in the argument class, or return null to use a default spacer
        /// </summary>
        public event StackedLayoutSpacerRequiredEvent SpacerRequired
        {
            add { _spacerRequired += value; }
            remove { _spacerRequired -= value; }
        }

        public delegate void WillAddFieldlabelEvent(object sender, FieldLabelEventArgs e);

        private WillAddFieldlabelEvent _willAddFieldLabel;

        /// <summary>
        /// Fired when a field label is about to be added to the layout
        /// </summary>
        public event WillAddFieldlabelEvent WillAddFieldLabel
        {
            add { _willAddFieldLabel += value; }
            remove { _willAddFieldLabel -= value; }
        }


        public delegate void WillAddInputControlEvent(object sender, InputControlEventArgs e);

        private WillAddInputControlEvent _willAddInputControl;
        private FieldLabelPositionType _fieldLabelPosition;

        /// <summary>
        /// Fired when an input control is about to be added to the layout
        /// </summary>
        public event WillAddInputControlEvent WillAddInputControl
        {
            add { _willAddInputControl += value; }
            remove { _willAddInputControl -= value; }
        }

        #endregion



        public void Initialise(Dictionary<FieldDefinitions.BaseFieldDefinition, System.Windows.Controls.Control> containerControls, Dictionary<FieldDefinitions.BaseFieldDefinition, System.Windows.Controls.Control> fieldLabels)
        {
            _containerControls = containerControls;
            _fieldLabels = fieldLabels;

            _controlsGrid = new Grid()
            {
                Visibility = System.Windows.Visibility.Visible,
                HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch,
                Background = System.Windows.Media.Brushes.Transparent
            };
            FormPanel.Content = _controlsGrid;

            UpdateControlMargins();
        }



        private void UpdateControlMargins()
        {
            foreach (Control inputControl in _containerControls.Values)
            {
                inputControl.Margin = new Thickness(ColumnSpacing / 2, RowSpacing / 2, ColumnSpacing / 2, RowSpacing / 2);
            }
            foreach (Control fieldLabel in _fieldLabels.Values)
            {
                fieldLabel.Margin = new Thickness(ColumnSpacing / 2, RowSpacing / 2, ColumnSpacing / 2, RowSpacing / 2);
            }
        }



        public void CreateLayout()
        {
            _controlsGrid.Children.Clear();
            // Create necessary row and column definitions
            _controlsGrid.ColumnDefinitions.Clear();
            _controlsGrid.RowDefinitions.Clear();

            ColumnDefinition colDef = new ColumnDefinition()
            {
                Width = new GridLength(1, GridUnitType.Auto)
            };
            _controlsGrid.ColumnDefinitions.Add(colDef);
            if (FieldLabelPosition == FieldLabelPositionType.Left)
            {
                colDef = new ColumnDefinition()
                {
                    Width = new GridLength(1, GridUnitType.Star)
                };
                _controlsGrid.ColumnDefinitions.Add(colDef);
            }

            // This is the minimum number of rows, there may be more if any field definitions are Newline=True
            int rowCount = _containerControls.Count;
            if (FieldLabelPosition == FieldLabelPositionType.Above)
            {
                // Labels above fields requires double the number of rows
                rowCount *= 2;
            }
            for (int loop = 0; loop < rowCount; loop++)
            {
                _controlsGrid.RowDefinitions.Add(new RowDefinition());
            }
            int currentRow = 0;
            foreach (BaseFieldDefinition fieldDefinition in _containerControls.Keys)
            {
                Control containerControl = null;
                Control fieldLabel = null;
                if (_containerControls.ContainsKey(fieldDefinition))
                {
                    containerControl = _containerControls[fieldDefinition];
                }
                if (_fieldLabels.ContainsKey(fieldDefinition))
                {
                    fieldLabel = _fieldLabels[fieldDefinition];
                }
                if (fieldLabel != null)
                {
                    //if (fieldLabel.Parent != null)
                    //{
                    //    // TODO: Check the creation/recreation mechanism
                    //    // This terrible kludge gets around a bug in the way forms are recreated when used on a TabControl
                    //    (fieldLabel.Parent as Panel).Children.Remove(fieldLabel);
                    //} 
                    _controlsGrid.Children.Add(fieldLabel);
                    Grid.SetRow(fieldLabel, currentRow);
                    Grid.SetColumn(fieldLabel, 0);
                }
                if (FieldLabelPosition == FieldLabelPositionType.Above)
                {
                    currentRow++;
                }
                if (containerControl != null)
                {
                    //if (containerControl.Parent != null)
                    //{
                    //    // TODO: Check the creation/recreation mechanism
                    //    // This terrible kludge gets around a bug in the way forms are recreated when used on a TabControl
                    //    (containerControl.Parent as Panel).Children.Remove(containerControl);
                    //} 
                    _controlsGrid.Children.Add(containerControl);
                    Grid.SetRow(containerControl, currentRow);
                    Grid.SetColumn(containerControl, FieldLabelPosition == FieldLabelPositionType.Left ? 1 : 0);
                }
                if (fieldDefinition.Newline)
                {
                    _controlsGrid.RowDefinitions.Add(new RowDefinition());
                    currentRow++;
                    Control spacer = ObtainSpacerControl(fieldDefinition);
                    Grid.SetRow(spacer, currentRow);
                    Grid.SetColumn(spacer, 0);
                    Grid.SetColumnSpan(spacer, 2);
                    _controlsGrid.Children.Add(spacer);
                }
                currentRow++;
            }
        }



        public void LayoutChanged()
        {

        }


        /// <summary>
        /// Return a control to use as a spacer, either from the SpacerRequired event or using the default
        /// </summary>
        /// <param name="forFieldDefinition">The field defintion object which requested the spacer control</param>
        /// <returns>Control</returns>
        private Control ObtainSpacerControl(BaseFieldDefinition forFieldDefinition)
        {
            Control result = null;
            if (_spacerRequired != null)
            {
                StackedLayoutSpacerRequiredEventArgs e = new StackedLayoutSpacerRequiredEventArgs(forFieldDefinition);
                _spacerRequired(this, e);
                if (e.SpacerControl == null)
                {
                    result = new ContentControl();
                    ((ContentControl)result).Content = new Rectangle()
                    {
                        Height = 2,
                        Fill = Brushes.LightGray,
                        Margin = new Thickness(ColumnSpacing / 2, RowSpacing / 2, ColumnSpacing / 2, RowSpacing / 2)
                    };
                }
                else
                {
                    result = e.SpacerControl;
                }
            }
            return result;
        }



        public void DropLayout()
        {
            _controlsGrid.Children.Clear();
            _containerControls.Clear();
            _fieldLabels.Clear();
        }
    }
}
